﻿using System;
using System.Diagnostics;
using System.Text;
using RabbitMQ.Client;
using RabbitMQDemo.Common;

namespace RabbitMQDemo.Producer
{
    public class Confirm
    {
        private const string ExchangeName = "confirm_exchange";

        private const string QueueName = "confirm_queue";

        private const string RoutingKey = "confirm_routing";

        public static void Send(string[] args)
        {
            var type = Utils.GetFirstValue(args);

            using var connection = MQClient.GetConnection();
            using var channel = connection.CreateModel();

            // 声明交换机
            channel.ExchangeDeclare(exchange: ExchangeName, type: "direct", durable: false, autoDelete: false, arguments: null);

            // 声明队列
            channel.QueueDeclare(queue: QueueName, durable: false, exclusive: false, autoDelete: false, arguments: null);
            channel.QueueBind(queue: QueueName, exchange: ExchangeName, routingKey: RoutingKey, arguments: null);

            switch (type)
            {
                case 1:
                    EventConfirm(channel);
                    break;
                default:
                    NormalConfirm(channel);
                    break;
            }
        }

        private static void NormalConfirm(IModel channel)
        {
            var sw = new Stopwatch();
            sw.Start();

            // 开启消息确认模式
            channel.ConfirmSelect();

            var index = 0;
            while (index < 10)
            {
                // 发送消息
                channel.BasicPublish(exchange: ExchangeName, routingKey: RoutingKey + "1", mandatory: false, basicProperties: null, body: GetMessage(index));

                Console.WriteLine("Send confirm message");
                index++;
            }

            // channel.waitForConfirmsOrDie(); // 直到所有信息都发布，只要有一个未确认就会IOException
            if (channel.WaitForConfirms())
            {
                Console.WriteLine("ok");
            }
            else
            {
                Console.WriteLine("error");
            }

            sw.Stop();
            Console.WriteLine($"over. ElapsedMilliseconds={sw.ElapsedMilliseconds}");
        }

        private static void EventConfirm(IModel channel)
        {
            var sw = new Stopwatch();
            sw.Start();

            // 开启消息确认模式
            channel.ConfirmSelect();

            // 消息发送成功的时候进入到这个事件：即RabbitMq服务器告诉生产者，我已经成功收到了消息
            channel.BasicAcks += (sender, e) =>
            {
                Console.WriteLine($"send message success: Acks. ElapsedMilliseconds={sw.ElapsedMilliseconds}");
            };

            // 消息发送失败的时候进入到这个事件：即RabbitMq服务器告诉生产者，你发送的这条消息我没有成功的投递到Queue中，或者说我没有收到这条消息。
            channel.BasicNacks += (sender, e) =>
            {
                Console.WriteLine($"send message fail: Nacks.. ElapsedMilliseconds={sw.ElapsedMilliseconds}");
            };

            // 监听一些不可达的消息：例如exchange不存在或者指定的routingKey路由不到
            channel.BasicReturn += (sender, e) =>
            {
                Console.WriteLine($"Send message failed. Code={e.ReplyCode}. Text={e.ReplyText}. Body={Encoding.UTF8.GetString(e.Body.Span)}");
            };

            var index = 0;
            while (index < 10)
            {
                // 发送消息
                channel.BasicPublish(exchange: ExchangeName, routingKey: RoutingKey + "1", mandatory: true, basicProperties: null, body: GetMessage(index));

                Console.WriteLine("Send confirm message");
                index++;
            }
        }

        private static byte[] GetMessage(int? i = null) => Encoding.UTF8.GetBytes($"[Confirm]{(i.HasValue ? $"[{i}]" : string.Empty)} message");
    }
}
